package vip.mtdp.asset.core.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.mtdp.asset.api.constants.AccountLabelEnum;
import vip.mtdp.asset.api.constants.AccountTypeEnum;
import vip.mtdp.asset.api.constants.AssetErrorCodeEnum;
import vip.mtdp.asset.api.dto.AccountDTO;
import vip.mtdp.asset.core.constants.AssetConstant;
import vip.mtdp.asset.core.constants.PostingMethodEnum;
import vip.mtdp.asset.core.constants.PostingStateEnum;
import vip.mtdp.asset.core.dto.JournalDTO;
import vip.mtdp.asset.core.manager.AccountManager;
import vip.mtdp.common.exception.AppRTException;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @Description 保存分录&明细，余额变更
 * @Author Evans.wang
 * @since 2023/10/30 10:35
 **/
@Slf4j
@Service
public class PostingService {

    @Autowired
    private AccountManager accountManager;


    /**
     * 记账
     * @param journalDTO
     */
    @Transactional
    public void posting(JournalDTO journalDTO){
        //根据分录明细变更账户余额
        List<JournalDTO.JournalItemDTO> journalItemDTOList = journalDTO.getJournalItemDTOList();

        //特殊场景分录明细为空，不变账户余额，直接返回
        if (journalItemDTOList == null || journalItemDTOList.isEmpty()){
            journalDTO.setPostingState(PostingStateEnum.SUCCESS.getCode());
            return;
        }

        //按照accountId 排序，防止死锁
        Collections.sort(journalItemDTOList, new Comparator<JournalDTO.JournalItemDTO>() {
            @Override
            public int compare(JournalDTO.JournalItemDTO o1, JournalDTO.JournalItemDTO o2) {
                return o1.getAccountId().compareTo(o2.getAccountId());
            }
        });

        //实时记账
        if (PostingMethodEnum.INSTANT.getCode().equals(journalDTO.getPostingMethod())){
            instant(journalItemDTOList);
            journalDTO.setPostingState(PostingStateEnum.SUCCESS.getCode());
        }

        //延迟记账
        if (PostingMethodEnum.DELAY.getCode().equals(journalDTO.getPostingMethod())){
            journalDTO.setPostingState(PostingStateEnum.WAITING_ASYNC_POSTING.getCode());
        }
    }

    /**
     * 实时记账
     * @param journalItemDTOList
     */
    private void instant(List<JournalDTO.JournalItemDTO> journalItemDTOList){
        //更新余额
        for (JournalDTO.JournalItemDTO journalItemDTO : journalItemDTOList){
            AccountDTO.BalanceDTO balanceDTO = null;
            Long accountId = journalItemDTO.getAccountId();
            String coin = journalItemDTO.getCoin();
            try {
                balanceDTO = accountManager.queryBalanceLockByAccountId(accountId, coin);
            }catch (Exception ex){
                if (!(ex instanceof AppRTException) ||
                        !AssetErrorCodeEnum.BALANCE_NOT_EXIST.getCode().equals(((AppRTException) ex).getCode())){
                    log.error("===> instant posting ex", ex);
                    throw ex;
                }
                //余额不存在,新增
                log.warn("===> balance not exist. accountId = {}, coin = {}", accountId, coin);
                AccountDTO accountDTO = accountManager.queryAccountById(accountId);
                List<AccountDTO.BalanceDTO> balanceDTOList = new ArrayList<>();
                balanceDTO.setVaultId(accountDTO.getVaultId());
                balanceDTO.setAccountId(accountId);
                balanceDTO.setChain(accountDTO.getChain());
                balanceDTO.setCoin(coin);
                balanceDTO.setNo(accountDTO.getNo());
                balanceDTOList.add(balanceDTO);
                accountManager.createBalance(balanceDTOList);
            }

            //dr
            if (AssetConstant.DR.equals(journalItemDTO.getDrCr())) {
                journalItemDTO.setOpenBalance(balanceDTO.getBalanceDr());

                BigDecimal balanceDr = balanceDTO.getBalanceDr().add(journalItemDTO.getAmount());
                balanceDTO.setBalanceDr(balanceDr);

                journalItemDTO.setCloseBalance(balanceDr);

                BigDecimal balance = balanceDTO.getBalance().add(journalItemDTO.getAmount());
                balanceDTO.setBalance(balance);
            }
            //cr
            if (AssetConstant.CR.equals(journalItemDTO.getDrCr())){
                journalItemDTO.setOpenBalance(balanceDTO.getBalanceCr());

                BigDecimal balanceCr = balanceDTO.getBalanceCr().add(journalItemDTO.getAmount());
                balanceDTO.setBalanceCr(balanceCr);

                journalItemDTO.setCloseBalance(balanceCr);

                BigDecimal balance = balanceDTO.getBalance().subtract(journalItemDTO.getAmount());
                balanceDTO.setBalance(balance);
            }
            //更新账户余额
            boolean b = accountManager.updateBalance(balanceDTO);
            if (!b){
                throw new AppRTException("posting account balance fail.");
            }
        }
    }

}
